package com.shanlitech.bluetooth;

import android.graphics.Color;
import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.shanlitech.bluetooth.sample.SampleDatabase;
import com.shanlitech.bluetooth.sample.SampleRecorder;
import com.shanlitech.bluetooth.map.Map2D;
import com.shanlitech.bluetooth.map.MapMarker;
import com.shanlitech.lbs.sample.RealtimeSample;
import com.shanlitech.lbs.geometry.Point2D;
import com.shanlitech.lbs.geometry.Round2D;
import com.shanlitech.lbs.geometry.Segment2D;
import com.shanlitech.lbs.locator.LocateContext;
import com.shanlitech.lbs.locator.LocateResult;
import com.shanlitech.lbs.locator.RssLocator;
import com.shanlitech.lbs.map.LbsDatabase;
import com.shanlitech.lbs.types.AccelerationRecord;
import com.shanlitech.lbs.types.Location2D;
import com.shanlitech.lbs.types.RssiRecord;
import com.shanlitech.lbs.utils.Smooth;

import java.util.ArrayList;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link RealtimeFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class RealtimeFragment extends Fragment {
    private static final String TAG = "SLBluetooth";
    private class SampleAdapter extends BaseAdapter {
        private class Holder {
            public TextView name;
            public Button btnTest;
        }

        private final SampleDatabase database;

        public SampleAdapter(SampleDatabase database) {
            this.database = database;
        }

        @Override
        public int getCount() {
            return database.getRealtime().size();
        }

        @Override
        public Object getItem(int i) {
            return database.getRealtime(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View convertView, ViewGroup viewGroup) {
            LayoutInflater inflater = LayoutInflater.from(getContext());
            SampleAdapter.Holder holder = null;
            if( convertView == null ) {
                convertView = inflater.inflate(R.layout.sample_item,null);
                holder = new Holder();
                holder.name = convertView.findViewById(R.id.sample_file);
                holder.btnTest = convertView.findViewById(R.id.btn_test);
                convertView.setTag(holder);
            } else {
                holder = (Holder) convertView.getTag();
            }

            final RealtimeSample sample = database.getRealtime(i);
            if(sample != null) {
                holder.name.setText(sample.getName());
                holder.name.setOnClickListener(v -> {
                    //showSampleLocation(i);
                });
                holder.btnTest.setOnClickListener(v -> {
                    testSample(i);
                });
            }
            return convertView;
        }
    }

    private Map2D mMap;
    private Button mTestAll;
    private Button mSaveSample;
    private Button mRealTime;
    private BluetoothScanner mScanner;
    private AccelerationSensor mAcceleration;
    private RssLocator mLocator;
    private SampleRecorder mRecorder;
    private SampleAdapter mAdapter;

    public RealtimeFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment RealtimeFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static RealtimeFragment newInstance(String param1, String param2) {
        RealtimeFragment fragment = new RealtimeFragment();
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onResume() {
        super.onResume();
        mRealTime.setText(R.string.start);
    }

    @Override
    public void onPause() {
        super.onPause();
        stopRealtime();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_realtime, container, false);

        mMap = (Map2D) view.findViewById(R.id.map);
        mSaveSample = view.findViewById(R.id.button_save_realtime);
        mTestAll = view.findViewById(R.id.button_test_all_sample);
        mRealTime = (Button) view.findViewById(R.id.button_realtime);
        mScanner = new BluetoothScanner(getContext());
        mAcceleration = new AccelerationSensor(getContext());
        mLocator = new RssLocator(LbsDatabase.getInstance());
        mRecorder = new SampleRecorder(getContext());
        mAdapter = new SampleAdapter(SampleDatabase.getInstance(getContext()));
        ListView lv = view.findViewById(R.id.list_realtime);
        lv.setAdapter(mAdapter);

        mSaveSample.setEnabled(false);
        mTestAll.setOnClickListener(v -> {
            testAllSample();
        });
        mSaveSample.setOnClickListener(v -> {
            mSaveSample.setEnabled(false);
            if( ! mRecorder.isEmpty() ) {
                mRecorder.saveAsRealtime(AppContext.getFlat());
                mAdapter.notifyDataSetInvalidated();
            }
        });
        mRealTime.setOnClickListener(v -> {
            if( mScanner.isStart() ) {
                stopRealtime();
            } else {
                startRealtime();
            }
        });
        /*
        mShowTrack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //mTrack.smooth();
                //List<LocationRecord> track = mTrack.get();
                List<Segment2D> track = mRTLocator.getTrack().smoothLine();
                if( track != null && !track.isEmpty() ) {
                    showTrack(track);
                }
            }
        });
        mShowTrack.setEnabled(false);
         */
        mMap.initialize(false,MapMarker.TEXT_NONE);
        return view;
    }

    private void startRealtime() {
        if( mScanner.isStart() ) {
            return;
        }
        mScanner.start(this::onScan,AppContext.getFlat().getBeacons().keySet(),1000);
        mAcceleration.start();
        mLocator.reset();
        mRecorder.clear();
        mRealTime.setText(R.string.stop);

        mTestAll.setEnabled(false);
        mSaveSample.setEnabled(false);
    }

    private void stopRealtime() {
        if( mScanner.isStart() ) {
            mScanner.stop();
            mAcceleration.stop();
            mLocator.reset();
            mRealTime.setText(R.string.start);
        }
        if( ! mRecorder.isEmpty() ) {
            mSaveSample.setEnabled(true);
        }
        mTestAll.setEnabled(true);
    }

    private void showTrack(List<Location2D> track) {
        //Log.d(TAG,"show track " + track.size() + " lines");
        for(int i=0; i < track.size() - 1; ++i) {
            final Location2D l = track.get(i);
            final Location2D r = track.get(i+1);
            mMap.addDynamicLine(new Segment2D(l,r), Color.GREEN);
        }
    }

    private void onScan(List<RssiRecord> records) {
        final List<AccelerationRecord> accRecords = mAcceleration.getReset();
        Log.d(TAG,"compute error result=" + records.toString());
        mRecorder.addRssi(records);
        mRecorder.addAcc(accRecords);
        final LocateResult rc = mLocator.input(1000,records,accRecords);
        if( rc.result != LocateResult.SUCCESS ) {
            Log.d(TAG,"compute error result=" + rc.result);
            return;
        }

        LocateContext lc = mLocator.getContext();
        showLocateContext(lc,true,true);
        mMap.invalidate();
    }

    private void showLocateContext(LocateContext lc,boolean showRound,boolean showProcess) {
        mMap.clearDynamic();
        if( showRound ) {
            for (Round2D r : lc.getRounds()) {
                mMap.addDynamicRound(r, Color.GREEN);
            }
        }
        if( showProcess ) {
            for (Point2D point : lc.getProcess()) {
                mMap.addDynamicPoint(point, Color.DKGRAY);
            }
        }
        mMap.addDynamicPoint(lc.getLocation(),Color.RED); // 最终位置
    }

    private void showLocateResult(LocateResult rc) {
        if( rc.result != LocateResult.SUCCESS || rc.locations == null) {
            return;
        }
        final List<Location2D> track = Smooth.smooth(rc.locations,1.0);
        showTrack(track);
        float red = 0;
        float blue = 255;
        final float step = 255.f / rc.locations.size();
        for(Location2D loc : rc.locations) {
            mMap.addDynamicPoint(loc.x,loc.y,Color.argb(255,Math.round(red),0,Math.round(blue)));
            red += step;
            blue -= step;
        }
    }

    private void testSample(int idx) {
        if( mScanner.isStart() ) {
            return;
        }
        final RealtimeSample sample = SampleDatabase.getInstance(getContext()).getRealtime(idx);
        if( sample == null ) {
            return;
        }
        mLocator.reset();
        final LocateResult rc = mLocator.input(1000,sample.rssiRecords,sample.accRecords);
        mLocator.reset();
        mMap.clearDynamic();
        showLocateResult(rc);

        mMap.invalidate();
    }

    private void testAllSample() {
        if( mScanner.isStart() ) {
            return;
        }
        final List<RealtimeSample> samples = SampleDatabase.getInstance(getContext()).getRealtime();
        if( samples.isEmpty() ) {
            return;
        }
        final List<LocateResult> results = new ArrayList<>();
        for(RealtimeSample sample : samples) {
            mLocator.reset();
            final LocateResult rc = mLocator.input(1000,sample.rssiRecords,sample.accRecords);
            if( rc.result == LocateResult.SUCCESS && rc.locations != null ) {
                results.add(rc);
            }
        }
        mLocator.reset();

        mMap.clearDynamic();
        for(LocateResult rc : results) {
            assert rc.locations != null;
            showLocateResult(rc);
        }
        mMap.invalidate();
    }
}